﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Web.Security;
using AutoMapper;
using NTQOnlineExam.Services.CommandService.Orders;
using NTQOnlineExam.Services.EntityFW.Context;
using NTQOnlineExam.Services.EntityFW.Entity;
using NTQOnlineExam.Services.EntityFW.Repository;
using NTQOnlineExam.Services.Infrastructure;
using NTQOnlineExam.Services.Infrastructure.Utility;
using NTQOnlineExam.Services.Models;
using NTQOnlineExam.Services.ReportService.Account;
using NTQOnlineExam.Services.ReportService.Basket;
using NTQOnlineExam.Services.ReportService.Order;
using NTQOnlineExam.Services.ReportService.Portfolio;

namespace NTQOnlineExam.Services.CommandService
{
    public class OrderCommandService : IOrderCommandService
    {
        private readonly IBasketReportService _basketReportService;
        private readonly IPortfolioReportService _portfolioReportService;
        private readonly IUnitOfWork _unitOfWork;
        private readonly IPortfolioCommandService _portfolioCommandService;
        private readonly IGenericRepository<Order> _ordeRepository;
        private readonly IAccountReportService _accountReportService;
        public OrderCommandService(IBasketReportService basketReportService, IPortfolioReportService portfolioReportService, IUnitOfWork unitOfWork, IPortfolioCommandService portfolioCommandService, IGenericRepository<Order> ordeRepository, IAccountReportService accountReportService)
        {
            _basketReportService = basketReportService;
            _portfolioReportService = portfolioReportService;
            _unitOfWork = unitOfWork;
            _portfolioCommandService = portfolioCommandService;
            _ordeRepository = ordeRepository;
            _accountReportService = accountReportService;
        }

        public OrderDTO CreateNewOrder(CreateNewOrderCommand command)
        {
            Guard.ArgumentIsNotNull(command, "command");
            Guard.ArgumentIsNotNull(_accountReportService.GetCurrentUser(), "CurrentUser");
            var basket = _basketReportService.GetCurrentBasket();
            Guard.ArgumentIsNotNull(basket, "basket");
            Guard.Against<InvalidDataException>(basket == null || basket.BasketItems == null || !basket.BasketItems.Any(), "You don't have any items in your basket.");
            Guard.Against<InvalidDataException>(!_portfolioReportService.ValidateCurrentBasket(), "You already have term(s) in your portfolio.");
            var order = new Order();
            AssembleOrder(order, basket, command);
            _unitOfWork.Repository<Order>().Insert(order);
            _unitOfWork.Save();
            return Mapper.Map<Order, OrderDTO>(order);
        }

        public OrderDTO CreateNewOrderByPhone(CreateNewOrderCommand command)
        {
            Guard.ArgumentIsNotNull(command, "command");
            var order = new Order();
            GenerateOrder(order, command);
            _unitOfWork.Repository<Order>().Insert(order);
            _unitOfWork.Save();
            _portfolioCommandService.InsertPortfolioFromOrder(order);
            return Mapper.Map<Order, OrderDTO>(order);
        }

        public OrderDTO CompleteOrder(CompleteOrderCommand command)
        {
            Guard.ArgumentIsNotNull(command, "command");
            var order = _ordeRepository.GetByID(command.OrderId);
            Guard.ArgumentIsNotNull(order, "order");
            var basket = _basketReportService.GetCurrentBasket();
            try
            {
                _unitOfWork.BeginTransaction();
                order.Status = OrderStatus.New.GetHashCode();
                _unitOfWork.Repository<Order>().Update(order);
                if (command.DeleteShoppingBasket && basket != null)
                {
                    _unitOfWork.Repository<Basket>().Delete(basket.BasketId);
                }
                _portfolioCommandService.InsertPortfolioFromOrder(order);
                _unitOfWork.Save();
                _unitOfWork.Commit();
            }
            catch (Exception ex)
            {
                _unitOfWork.Rollback();
                throw new Exception(ex.Message, ex);
            }
            return Mapper.Map<Order, OrderDTO>(order);
        }

        #region Helpers

        private void AssembleOrder(Order order, BasketDTO basketDto, CreateNewOrderCommand command)
        {
            if (order == null || basketDto == null || command == null) throw new ArgumentException("Invalid arguments");
            order.OrderId = Guid.NewGuid();
            order.BillingAddress = command.BillingAddress;
            order.BillingCity = command.BillingCity;
            order.BillingCountry = command.BillingCountry;
            order.BillingZipCode = command.BillingZipCode;

            order.UserId = (int)_accountReportService.GetCurrentUser().ProviderUserKey;
            order.CreationDate = DateTime.Now;
            order.Email = command.Email;
            order.SubTotal = basketDto.GetTotalPrice();
            order.PaymentFee = basketDto.PaymentFee;
            order.PaymentFeeTax = basketDto.PaymentTaxFee;
            order.Status = OrderStatus.Draft.GetHashCode();
            order.OrderDetails = new List<OrderDetail>();
            foreach (var basketItem in basketDto.BasketItems)
            {
                var orderDetailItem = new OrderDetail()
                {
                    Id = Guid.NewGuid(),
                    OrderId = order.OrderId,
                    Order = order,
                    Price = basketItem.Price,
                    TermId = basketItem.TermId,
                    TermName = basketItem.TermName
                };
                order.OrderDetails.Add(orderDetailItem);
            }
        }

        private void GenerateOrder(Order order, CreateNewOrderCommand command)
        {
            if (order == null || command == null) throw new ArgumentException("Invalid arguments");
            order.OrderId = Guid.NewGuid();
            order.BillingAddress = command.BillingAddress;
            order.BillingCity = command.BillingCity;
            order.BillingCountry = command.BillingCountry;
            order.BillingZipCode = command.BillingZipCode;

            order.UserId = command.UserId;
            order.CreationDate = DateTime.Now;
            order.Email = command.Email;
            order.SubTotal = command.Term.Price ?? 0;
            order.PaymentFee = null;
            order.PaymentFeeTax = null;
            order.Status = OrderStatus.New.GetHashCode();
            order.OrderType = command.OrderType;
            order.TransactionNo = command.TransactionNo;
            order.OrderDetails = new List<OrderDetail>();

            var orderDetailItem = new OrderDetail()
                {
                    Id = Guid.NewGuid(),
                    OrderId = order.OrderId,
                    Order = order,
                    Price = command.Term.Price ?? 0,
                    TermId = command.Term.TermId,
                    TermName = command.Term.TermName
                };
            order.OrderDetails.Add(orderDetailItem);
        }

        #endregion
    }
}
